import {useEffect, useRef, useState} from "react";
import Taro, {createSelectorQuery, getSystemInfoSync} from "@tarojs/taro";

export interface VirtualListParams {
  data: any[];
  pageNo: number;
  /**
   * 自定义分段的数量，默认10
   */
  segmentNum?: number;
  listId?: string;
  /**
   * 自定义scrollView的参数，会合并到组件内部的scrollView的参数里
   */
  scrollViewProps?: any;
  /**
   * 指定页面显示区域基准值，例如2，则组件会监听 2 * scrollHeight高度的上下区域(该值会影响页面真实节点的渲染数量)
   */
  screenNum?: number;
}

export function useVirtualList({
                                 data,
                                 pageNo,
                                 segmentNum = 20,
                                 screenNum = 2,
                                 listId = "zt-virtial-list",
                                 scrollViewProps
                               }: VirtualListParams) {
  const [twoList, setTwoList] = useState<any[]>([])// 二维数组
  const windowHeight = useRef<number>(0); // / 当前屏幕的高度
  const twoListRef = useRef<any[]>([]); // 缓存twoList，因为小程序observe回调里面无法拿到最新的state
  const initList = useRef<any[]>([]); // 承载初始化的二维数组
  const pageHeightArr = useRef<number[]>([])// 用来装每一屏的高度
  const currentPage = useRef(Taro.getCurrentInstance()); // 当前页面实例

  useEffect(() => {
    windowHeight.current = getSystemInfoSync().windowHeight;
  }, []);


  useEffect(() => {
    formatMultiList(data);
  }, [data])

  useEffect(() => {
    twoListRef.current = [...twoList]
  }, [twoList])

  /**
   * 当list是通过服务端分页获取的时候，对list进行处理
   * @param  list 外部list
   * @param  pageNum 当前页码
   */
  const formatMultiList = (list: any[] = []) => {
    if (!list?.length) return;

    const pageNum = Math.max((pageNo - 2), 0); // 每一屏为一个单位，屏幕索引

    segmentList(list); // 分割列表

    twoList[pageNum] = initList.current[pageNum];

    setTwoList([...twoList])

    Taro.nextTick(() => {
      setHeight()
    })
  }

  /**
   * 设置每一个维度的数据渲染完成之后所占的高度
   */
  const setHeight = () => {
    const query = createSelectorQuery()
    const wholePageIndex = Math.max((pageNo - 2), 0); // 每一屏为一个单位，屏幕索引
    query.select(`#${listId} .wrap_${wholePageIndex}`).boundingClientRect()
    query.exec((res) => {
      // 有数据的时候才去收集高度，不然页面初始化渲染（在H5中无数据）收集到的高度是错误的
      if (initList.current?.length) {
        pageHeightArr.current.push(res?.[0]?.height)
      }
    })
    miniObserve()
  }

  /**
   * 按规则分割list，存在initList上，备用
   */
  const segmentList = (list: any[] = []) => {
    let arr: any[] = []
    const _list: any[] = []

    list.forEach((item, index) => {
      arr.push(item)
      if ((index + 1) % segmentNum === 0) {
        _list.push(arr)
        arr = []
      }
    })

    // 将分段不足segmentNum的剩余数据装入_list
    if (arr.length) {
      _list.push(arr)
    }
    initList.current = _list
  }

  /**
   * 小程序平台监听
   */
  const miniObserve = () => {
    const currentIndex = Math.max((pageNo - 2), 0);
    // 以传入的scrollView的高度为相交区域的参考边界，若没传，则默认使用屏幕高度
    const scrollHeight = scrollViewProps?.style?.height || windowHeight.current
    const observer = Taro.createIntersectionObserver(currentPage.current.page!).relativeToViewport({
      top: screenNum * scrollHeight,
      bottom: screenNum * scrollHeight,
    })

    observer.observe(`#${listId} .wrap_${currentIndex}`, (res) => {
      const twoList = twoListRef.current;

      if (res?.intersectionRatio <= 0) {
        // 当没有与当前视口有相交区域，则将改屏的数据置为该屏的高度占位
        twoList[currentIndex] = {height: pageHeightArr.current[currentIndex]}

        setTwoList([...twoList])
      } else if (!twoList[currentIndex]?.length) {
        // 如果有相交区域，则将对应的维度进行赋值
        twoList[currentIndex] = initList.current[currentIndex]

        setTwoList([...twoList])
      }
    })
  }

  return [twoList]
}
